package com.lidy.tankbattle.tankgame2.panel;

import com.lidy.tankbattle.tankgame2.bomb.Bomb;
import com.lidy.tankbattle.tankgame2.bullet.Bullet;
import com.lidy.tankbattle.tankgame2.bullet.BulletDetection;
import com.lidy.tankbattle.tankgame2.constant.BombPriorities;
import com.lidy.tankbattle.tankgame2.constant.BulletPriorities;
import com.lidy.tankbattle.tankgame2.constant.TankPriorities;
import com.lidy.tankbattle.tankgame2.constant.WallPriorities;
import com.lidy.tankbattle.tankgame2.tank.*;
import com.lidy.tankbattle.tankgame2.wall.Wall;
import com.lidy.tankbattle.tankutils.ThreadUtil;
import com.lidy.tankbattle.tankutils.WallUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.time.Duration;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lidy
 * @version V2.0
 * @classname TankPanel2
 * @description 游戏操作界面
 * @date 2022/5/28 15:29
 */
public class TankPanel extends JPanel implements KeyListener, Runnable, MouseListener {

    /**
     * 己方坦克
     **/
    private Hero hero;

    /**
     * 己方坦克是否存活
     **/
    private int playerTankLifeTimes = TankPriorities.PLAYER_TANK_LIFE_TIMES;

    /**
     * 敌方坦克
     **/
    private final ConcurrentHashMap<Integer, EnemyTank> enemyTanks = new ConcurrentHashMap<>(TankPriorities.ENEMY_TANK_TOTAL_NUMBER + 2);

    /**
     * 已生成敌方坦克数量
     **/
    private int createdEnemyTankNumber = 0;

    /**
     * 新的敌方坦克创建位置
     **/
    private EntityLocation createEnemyTankLocation = new EntityLocation(550, 600, 0, 50);

    /**
     * 炸弹
     **/
    private final ConcurrentHashMap<Integer, Bomb> bombs = new ConcurrentHashMap<>(10);

    /**
     * 游戏是否进行中
     **/
    public static Boolean gameContinue = true;

    /**
     * 游戏暂停,0-游戏未暂停，1-游戏胜利，2-游戏失败
    **/
    public static int gamePause = 0;

    /**
     * 图上所有坦克分布位置
     **/
    private ConcurrentHashMap<Integer, EntityLocation> tanksLocations = new ConcurrentHashMap<>(TankPriorities.ENEMY_TANK_TOTAL_NUMBER + 2);

    /**
     * 图上所有墙体
     **/
    private final ConcurrentHashMap<Integer, Wall> walls = new ConcurrentHashMap<>(100);

    /**
     * 画布宽度
     **/
    private final int PANEL_WIDTH;

    /**
     * 画布长度
     **/
    private final int PANEL_HEIGHT;

    /**
     * 统计信息
     **/


    public TankPanel(int panelWidth, int panelHeight) {
        this.PANEL_WIDTH = panelWidth;
        this.PANEL_HEIGHT = panelHeight;
        addTanks();
        addWalls();
    }

    /**
     * @author: lidy
     * @description: 添加坦克
     * @date: 2022/6/2 9:55
     */
    public void addTanks() {
        // 设置我方坦克
        hero = Hero.getHero(false);
        tanksLocations.put(hero.getNumber(), TankMove.getSingleTankLocation(hero));
        // 设置敌方坦克
        for (int i = 0; i < TankPriorities.ENEMY_TANK_CONCURRENT_EXIST_NUMBER; i++) {
            createEnemyTank(100 * i, 0, false);
        }
        // 启动一个线程控制所有敌方坦克移动
        ControlEnemyTanksMoving controlEnemyTanksMoving = new ControlEnemyTanksMoving();
        ThreadUtil.threadPoolExecutor.execute(controlEnemyTanksMoving);
        // 启动一个线程控制子弹撞击坦克、墙体
        ControlBullets controlBullets = new ControlBullets();
        ThreadUtil.threadPoolExecutor.execute(controlBullets);
    }

    /**
     * @author: lidy
     * @description: 创建敌方坦克
     * @date: 2022/6/12 9:38
     */
    private void createEnemyTank(int x, int y, Boolean invincible) {
        EnemyTank enemyTank = EnemyTank.getEnemyTank(x, y, createdEnemyTankNumber, LocalTime.now(), invincible);
        enemyTanks.put(enemyTank.getNumber(), enemyTank);
        tanksLocations.put(enemyTank.getNumber(), TankMove.getSingleTankLocation(enemyTank));
        // 启动坦克自动射击
        ThreadUtil.threadPoolExecutor.execute(enemyTank.getShot());
        createdEnemyTankNumber++;
    }

    /**
     * @author: lidy
     * @description: 添加墙体
     * @date: 2022/6/5 12:40
     */
    public void addWalls() {
        List<Wall> wallList = WallUtils.readWallPicture1();
        for (Wall wall : wallList) {
            walls.put(wall.getNumber(), wall);
        }
    }

    /**
     * @author: lidy
     * @description: 面板画图区
     * @date: 2022/6/2 9:55
     */
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
        // 画玩家的坦克和子弹
        drawPlayerTankAndBullets(g);
        // 画敌方坦克和子弹
        drawEnemyTanksAndBullets(g);
        // 画炸弹
        drawBombImages(g);
        // 画墙体
        drawWalls(g);
        // TODO 画右侧统计信息
        drawStatistics(g);
        if (gamePause != 0) {
            drawVictoryOrFail(g, gamePause == 1);
        }
    }

    /**
     * @author: lidy
     * @description: 画玩家坦克和子弹
     * @date: 2022/6/12 12:04
     */
    private void drawPlayerTankAndBullets(Graphics g) {
        if (!hero.getLive()) {
            if (playerTankLifeTimes <= 0) {
                gamePause = 2;
            } else {
                hero = Hero.getHero(true);
                playerTankLifeTimes--;
                tanksLocations.put(hero.getNumber(), TankMove.getSingleTankLocation(hero));
            }
        }
        boolean drawOrNot = true;
        if (hero.getInvincible()) {
            Duration duration = Duration.between(hero.getCreatedTime(), LocalTime.now());
            hero.setInvincible(duration.getSeconds() <= TankPriorities.TANK_INVINCIBLE_TIME);
            Random random = new Random();
            drawOrNot = random.nextInt() % 2 == 0;
        }
        if (drawOrNot) {
            drawTankImages(g, hero);
        }
        // 画玩家坦克的子弹
        drawBulletsImages(g, hero.getBullets());
    }

    /**
     * @author: lidy
     * @description: 画胜利标识
     * @date: 2022/6/12 13:03
     */
    private void drawVictoryOrFail(Graphics g, boolean type) {
        String str = type ? "胜利！" : "失败！";
        Font font = new Font("华文楷体", Font.BOLD + Font.ITALIC, 150);
        g.setFont(font);
        g.setColor(new Color(Color.RED.getRGB()));
        g.drawString(str, 150, 300);
    }

    /**
     * @author: lidy
     * @description: 画敌方坦克
     * @date: 2022/6/12 10:31
     */
    private void drawEnemyTanksAndBullets(Graphics g) {
        enemyTanks.forEach((number, enemyTank) -> {
            if (!enemyTank.getLive()) {
                enemyTanks.remove(number);
                tanksLocations.remove(number);
            } else {
                boolean drawOrNot = true;
                if (enemyTank.getInvincible()) {
                    Random random = new Random();
                    drawOrNot = random.nextInt() % 2 == 0;
                }
                if (drawOrNot) {
                    drawTankImages(g, enemyTank);
                }
                drawBulletsImages(g, enemyTank.getBullets());
            }
        });
        if (enemyTanks.size() < TankPriorities.ENEMY_TANK_CONCURRENT_EXIST_NUMBER
                && createdEnemyTankNumber < TankPriorities.ENEMY_TANK_TOTAL_NUMBER) {
            boolean f = false;
            for (Map.Entry<Integer, EntityLocation> entry : tanksLocations.entrySet()) {
                if (TankMove.twoRectanglesIntersectionDetect(createEnemyTankLocation, entry.getValue())) {
                    f = true;
                    break;
                }
            }
            if (!f) {
                createEnemyTank(550, 0, true);
            }
        }
        if (createdEnemyTankNumber == TankPriorities.ENEMY_TANK_TOTAL_NUMBER && enemyTanks.size() == 0) {
            // drawVictoryOrFail(g, true);
            gamePause = 1;
        }
    }

    /**
     * @author: lidy
     * @description: 画墙体
     * @date: 2022/6/6 20:11
     */
    private void drawWalls(Graphics g) {
        walls.forEach((i, wall) -> {
            if (wall != null && wall.getExist()) {
                String address = wall.getType() == WallPriorities.BRICK_WALL ? WallPriorities.BRICK_WALL_IMAGE_ADDRESS : WallPriorities.METAL_WALL_IMAGE_ADDRESS;
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                Image image = toolkit.getImage(TankPanel.class.getResource(address));
                g.drawImage(image, wall.getX(), wall.getY(), WallPriorities.WALL_WIDTH, WallPriorities.WALL_HEIGHT, this);
            } else {
                walls.remove(i);
            }
        });
    }

    /**
     * @author: lidy
     * @description: 画炸弹
     * @date: 2022/6/2 19:17
     */
    private void drawBombImages(Graphics g) {
        bombs.forEach((number, bomb) -> {
            if (bomb != null && bomb.getLive()) {
                String address = bomb.getBombAddress().get(BombPriorities.BOMB_LEFT - bomb.getLeft());
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                Image image = toolkit.getImage(TankPanel.class.getResource(address));
                g.drawImage(image, bomb.getX(), bomb.getY(), BombPriorities.BOMB_WIDTH, BombPriorities.BOMB_LENGTH, this);
                bomb.reduceBombLeft();
            } else {
                bombs.remove(number);
            }
        });
    }

    /**
     * @author: lidy
     * @description: 画子弹（图片）
     * @date: 2022/6/2 16:43
     */
    public void drawBulletsImages(Graphics g, ConcurrentHashMap<Bullet, Integer> bullets) {
        bullets.forEach((bullet, i) -> {
            if (bullet.getLive()) {
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                Image image = toolkit.getImage(TankPanel.class.getResource(bullet.getImageAddress()));
                g.drawImage(image, bullet.getX(), bullet.getY(), BulletPriorities.BULLET_WIDTH, BulletPriorities.BULLET_LENGTH, this);
            }
        });
    }

    /**
     * @author: lidy
     * @description: 画坦克图片
     * @date: 2022/6/2 16:09
     */
    public void drawTankImages(Graphics g, Tank tank) {
        if (!tank.getLive()) {
            return;
        }
        int x = tank.getX(), y = tank.getY(), direction = tank.getDirection();
        String address;
        switch (direction) {
            // 向下
            case TankPriorities.DIRECTION_DOWN -> address = tank.getDownImageAddress();
            // 向左
            case TankPriorities.DIRECTION_LEFT -> address = tank.getLeftImageAddress();
            // 向右
            case TankPriorities.DIRECTION_RIGHT -> address = tank.getRightImageAddress();
            // 向上
            default -> address = tank.getUpImageAddress();
        }
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Image image = toolkit.getImage(TankPanel.class.getResource(address));
        g.drawImage(image, x, y, tank.getWidth(), tank.getHeight(), this);
    }

    /**
     * @author: lidy
     * @description: 画统计信息
     * @date: 2022/6/12 11:24
     */
    private void drawStatistics(Graphics g) {
        String playerRemainderLife = "剩余生命：" + playerTankLifeTimes;
        String enemyTanksNum = "剩余敌机：" + (TankPriorities.ENEMY_TANK_TOTAL_NUMBER - createdEnemyTankNumber + enemyTanks.size());
        String annihilateEnemyTankNum = "歼敌数量：" + (createdEnemyTankNumber - enemyTanks.size());
        List<String> drawStrings = List.of(playerRemainderLife, annihilateEnemyTankNum, enemyTanksNum);
        Font font = new Font("楷体", Font.PLAIN, 25);
        g.setFont(font);
        int startX = 605;
        int startY = 25;
        int interval = 30;
        for (int i = 0; i < drawStrings.size(); i++) {
            g.drawString(drawStrings.get(i), startX, startY + i * interval);
        }
    }

    /**
     * @author: lidy
     * @description: 有字符输出时，该方法会触发
     * @date: 2022/6/2 9:56
     */
    @Override
    public void keyTyped(KeyEvent e) {

    }

    /**
     * @author: lidy
     * @description: 当某个键按下时，该方法会触发
     * @date: 2022/6/2 9:56
     */
    @Override
    public void keyPressed(KeyEvent e) {
        if (gameContinue && gamePause == 0) {
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                TankMove.heroTankMove(TankPriorities.DIRECTION_DOWN, hero, enemyTanks, tanksLocations, walls, bombs);
            } else if (e.getKeyCode() == KeyEvent.VK_UP) {
                TankMove.heroTankMove(TankPriorities.DIRECTION_UP, hero, enemyTanks, tanksLocations, walls, bombs);
            } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                TankMove.heroTankMove(TankPriorities.DIRECTION_LEFT, hero, enemyTanks, tanksLocations, walls, bombs);
            } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                TankMove.heroTankMove(TankPriorities.DIRECTION_RIGHT, hero, enemyTanks, tanksLocations, walls, bombs);
            }
            // 按下Q键发射子弹
            if (e.getKeyCode() == KeyEvent.VK_Q) {
                if (hero.getLive()) {
                    hero.shot();
                }
            }
        }
    }

    /**
     * @author: lidy
     * @description: 当某个键放开时，该方法会触发
     * @date: 2022/6/2 9:56
     */
    @Override
    public void keyReleased(KeyEvent e) {

    }

    /**
     * @author: lidy
     * @description: 监控鼠标点击事件
     * @date: 2022/6/12 13:17
     */
    @Override
    public void mouseClicked(MouseEvent e) {
        if (gameContinue) {
            if (gamePause != 0) {
                // TODO 鼠标点击
            }
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    /**
     * @author: lidy
     * @description: 控制所有坦克移动线程
     * @date: 2022/6/4 12:49
     */
    class ControlEnemyTanksMoving implements Runnable {
        @Override
        public void run() {
            while (gameContinue) {
                try {
                    Thread.sleep(150);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                enemyTanks.forEach((number, tank) -> {
                    int moveResult = TankMove.enemyTankMove(tank, tanksLocations, walls);
                    if (moveResult == 1) {
                        if (!tank.getInvincible()) {
                            tank.reduceBlood(TankPriorities.TANKS_BUMP_BLOOD_REDUCED);
                        }
                        if (!hero.getInvincible()) {
                            hero.reduceBlood(TankPriorities.TANKS_BUMP_BLOOD_REDUCED);
                        }
                        if (!tank.getLive()) {
                            bombs.put(tank.getNumber(), Bomb.getTankBomb(tank.getX(), tank.getY()));
                        }
                        if (!hero.getLive()) {
                            bombs.put(hero.getNumber(), Bomb.getTankBomb(hero.getX(), hero.getY()));
                            tanksLocations.remove(TankPriorities.MY_TANK_NUMBER);
                        }
                    }
                });
            }
        }
    }

    /**
     * @author: lidy
     * @description: 监控所有子弹撞击坦克或墙壁
     * @date: 2022/6/7 20:10
     */
    class ControlBullets implements Runnable {
        @Override
        public void run() {
            while (gameContinue) {
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                BulletDetection.detectBulletsResult(hero, enemyTanks, walls, bombs, tanksLocations);
            }
        }
    }


    /**
     * @author: lidy
     * @description: 50mm重绘一次面板
     * @date: 2022/6/1 16:57
     */
    @Override
    public void run() {
        while (gameContinue) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            this.repaint();
        }
    }

}
